package util

import (
	"bytes"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"image-server/config"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

// OSS相关常量和错误定义
const (
	ossBatchSize = 100 // 可配置的批量操作大小
)

var (
	ErrOSSClientInit  = errors.New("OSS客户端初始化失败")
	ErrObjectNotFound = errors.New("对象不存在")
)

// 全局OSS客户端单例
var (
	globalBucket *oss.Bucket
	bucketOnce   sync.Once
	bucketErr    error
)

// 初始化Bucket（全局单例，优化性能）
func getBucket() (*oss.Bucket, error) {
	bucketOnce.Do(func() {
		// 从config中获取
		client, err := config.NewOSSClient()
		if err != nil {
			bucketErr = fmt.Errorf("%w: %v", ErrOSSClientInit, err)
			return
		}
		globalBucket, bucketErr = client.Bucket(config.Aliyun.Bucket)
		if bucketErr != nil {
			bucketErr = fmt.Errorf("获取Bucket失败: %w", bucketErr)
		}
	})
	return globalBucket, bucketErr
}

// GenerateOSSSignedURL 生成OSS资源的临时签名URL
func GenerateOSSSignedURL(objectKey string, expiration time.Duration) (string, error) {
	return generateSignedURL(objectKey, int64(expiration.Seconds()))
}

// UploadToOSS 上传文件到OSS
func UploadToOSS(ossKey string, content []byte) error {
	return uploadObject(ossKey, content)
}

// DeleteFromOSS 从OSS删除文件
func DeleteFromOSS(ossKey string) error {
	return deleteObject(ossKey)
}

// GetSignedURL 获取带签名的OSS访问URL
func GetSignedURL(ossKey string, expiredSeconds int64) (string, error) {
	return generateSignedURL(ossKey, expiredSeconds)
}

// RenameOSSFolder 重命名OSS文件夹（批量移动对象）
func RenameOSSFolder(oldFolder, newFolder string) error {
	if oldFolder == "" || newFolder == "" {
		return fmt.Errorf("%w: 源文件夹和目标文件夹路径不能为空", ErrInvalidParam)
	}

	oldFolder = normalizeFolderPath(oldFolder)
	newFolder = normalizeFolderPath(newFolder)

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	// 记录已复制的对象，用于失败回滚
	var copiedObjects []string
	defer func() {
		if err != nil && len(copiedObjects) > 0 {
			// 回滚：删除已复制的对象
			_ = deleteObjects(copiedObjects)
		}
	}()

	marker := ""
	for {
		resp, err := bucket.ListObjects(
			oss.Prefix(oldFolder),
			oss.Marker(marker),
			oss.MaxKeys(ossBatchSize),
		)
		if err != nil {
			return fmt.Errorf("列出文件夹内容失败: %w", err)
		}

		for _, obj := range resp.Objects {
			newKey := strings.Replace(obj.Key, oldFolder, newFolder, 1)
			if newKey == obj.Key {
				continue
			}

			// 复制对象
			if _, err := bucket.CopyObject(obj.Key, newKey); err != nil {
				return fmt.Errorf("复制对象 %s 失败: %w", obj.Key, err)
			}
			copiedObjects = append(copiedObjects, newKey)

			// 删除原对象
			if err := bucket.DeleteObject(obj.Key); err != nil {
				return fmt.Errorf("删除原对象 %s 失败: %w", obj.Key, err)
			}
		}

		if !resp.IsTruncated {
			break
		}
		marker = resp.NextMarker
	}

	// 删除原文件夹对象
	if err := bucket.DeleteObject(oldFolder); err != nil {
		return fmt.Errorf("删除原文件夹对象失败: %w", err)
	}
	return nil
}

// DeleteOSSFolder 删除OSS文件夹及所有内容
func DeleteOSSFolder(folderPath string) error {
	if folderPath == "" {
		return fmt.Errorf("%w: 文件夹路径不能为空", ErrInvalidParam)
	}

	folderPath = normalizeFolderPath(folderPath)

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	marker := ""
	for {
		resp, err := bucket.ListObjects(
			oss.Prefix(folderPath),
			oss.Marker(marker),
			oss.MaxKeys(ossBatchSize),
		)
		if err != nil {
			return fmt.Errorf("列出文件夹内容失败: %w", err)
		}

		var objectKeys []string
		for _, obj := range resp.Objects {
			objectKeys = append(objectKeys, obj.Key)
		}

		if len(objectKeys) > 0 {
			if err := deleteObjects(objectKeys); err != nil {
				return fmt.Errorf("批量删除对象失败: %w", err)
			}
		}

		if !resp.IsTruncated {
			break
		}
		marker = resp.NextMarker
	}

	return nil
}

// CreateOSSFolder 在OSS创建文件夹
func CreateOSSFolder(folderPath string) error {
	folderPath = normalizeFolderPath(folderPath)

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	return bucket.PutObject(folderPath, strings.NewReader(""))
}

// CheckOSSPathExists 检查OSS路径是否存在
func CheckOSSPathExists(path string) (bool, error) {
	bucket, err := getBucket()
	if err != nil {
		return false, err
	}

	exists, err := bucket.IsObjectExist(path)
	if err != nil {
		return false, fmt.Errorf("检查路径 %s 存在性失败: %w", path, err)
	}
	return exists, nil
}

// SetOSSFolderACL 设置OSS文件夹的访问权限（注意：仅对文件夹对象本身生效）
func SetOSSFolderACL(folderPath string, isPublic bool) error {
	folderPath = normalizeFolderPath(folderPath)

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	acl := oss.ACLPrivate
	if isPublic {
		acl = oss.ACLPublicRead
	}

	if err := bucket.SetObjectACL(folderPath, acl); err != nil {
		return fmt.Errorf("设置文件夹 %s ACL失败: %w", folderPath, err)
	}
	return nil
}

// DeleteObjects 批量删除OSS对象
func DeleteObjects(keys []string) error {
	if len(keys) == 0 {
		return nil // 空切片直接返回，避免无效调用
	}
	bucket, err := getBucket()
	if err != nil {
		return err
	}
	_, err = bucket.DeleteObjects(keys)
	if err != nil {
		return fmt.Errorf("OSS批量删除失败: %w", err)
	}
	return nil
}

// 内部：删除单个OSS对象（被DeleteFromOSS调用）
func deleteObject(objectKey string) error {
	if objectKey == "" {
		return fmt.Errorf("%w: OSS存储路径不能为空", ErrInvalidParam)
	}

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	// 先检查对象是否存在
	exists, err := bucket.IsObjectExist(objectKey)
	if err != nil {
		return fmt.Errorf("检查对象 %s 存在性失败: %w", objectKey, err)
	}
	if !exists {
		return fmt.Errorf("%w: %s", ErrObjectNotFound, objectKey)
	}

	if err := bucket.DeleteObject(objectKey); err != nil {
		return fmt.Errorf("删除对象 %s 失败: %w", objectKey, err)
	}
	return nil
}

// 内部：批量删除OSS对象
func deleteObjects(objectKeys []string) error {
	if len(objectKeys) == 0 {
		return nil
	}

	bucket, err := getBucket()
	if err != nil {
		return err
	}
	_, err = bucket.DeleteObjects(objectKeys)
	if err != nil {
		return fmt.Errorf("批量删除对象失败: %w", err)
	}
	return nil
}

// 内部：生成带签名的访问URL（被GenerateOSSSignedURL和GetSignedURL调用）
func generateSignedURL(objectKey string, expireSeconds int64) (string, error) {
	if objectKey == "" {
		return "", fmt.Errorf("%w: OSS存储路径不能为空", ErrInvalidParam)
	}
	if expireSeconds <= 0 {
		expireSeconds = 3600 // 默认1小时有效期
	}

	bucket, err := getBucket()
	if err != nil {
		return "", err
	}

	// 检查对象是否存在
	exists, err := bucket.IsObjectExist(objectKey)
	if err != nil {
		return "", fmt.Errorf("检查对象 %s 存在性失败: %w", objectKey, err)
	}
	if !exists {
		return "", fmt.Errorf("%w: %s", ErrObjectNotFound, objectKey)
	}

	signedURL, err := bucket.SignURL(objectKey, oss.HTTPGet, expireSeconds)
	if err != nil {
		return "", fmt.Errorf("生成签名URL失败: %w", err)
	}
	return signedURL, nil
}

// 内部：上传对象到OSS（被UploadToOSS调用）
func uploadObject(objectKey string, content []byte) error {
	if objectKey == "" {
		return fmt.Errorf("%w: OSS存储路径不能为空", ErrInvalidParam)
	}
	if len(content) == 0 {
		return fmt.Errorf("%w: 文件内容不能为空", ErrInvalidParam)
	}

	bucket, err := getBucket()
	if err != nil {
		return err
	}
	if err := bucket.PutObject(objectKey, bytes.NewReader(content)); err != nil {
		return fmt.Errorf("上传对象 %s 失败: %w", objectKey, err)
	}
	return nil
}

// 内部：规范化文件夹路径
func normalizeFolderPath(path string) string {
	if !strings.HasSuffix(path, "/") {
		return path + "/"
	}
	return path
}

// 重命名OSS单个文件
func RenameOSSObject(oldKey, newKey string) error {
	if oldKey == "" || newKey == "" || oldKey == newKey {
		return nil // 无效参数或路径相同直接返回
	}

	bucket, err := getBucket()
	if err != nil {
		return err
	}

	// 1. 复制文件到新路径
	if _, err := bucket.CopyObject(oldKey, newKey); err != nil {
		return fmt.Errorf("复制失败: %w", err)
	}

	// 2. 删除原文件（复制成功才执行）
	if err := bucket.DeleteObject(oldKey); err != nil {
		// 尝试回滚新文件
		_ = bucket.DeleteObject(newKey)
		return fmt.Errorf("删除原文件失败: %w", err)
	}

	return nil
}

// 清理文件名
func SanitizeFileName(name string) string {
	// 替换特殊字符为下划线
	sanitized := strings.Map(func(r rune) rune {
		if strings.ContainsRune(`/\:*?"<>|`, r) {
			return '_'
		}
		return r
	}, name)
	// 兜底处理空字符串
	if sanitized == "" {
		sanitized = "untitled"
	}
	return sanitized
}
